/***************************************************************************
 *   oboe.controllers.StateMachine                                         *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.controllers
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	
	import oboe.controllers.debugConsole.DebugConsole;
	import oboe.core.Controller;
	import oboe.core.Oboe;
	import oboe.math.Frame;
	
	public class StateMachine extends Controller
	{
		private var _baseTime : int;
		private var lastFPSTraceTime : int;
		private var frameCount : int;
		private var previousFPS : int = -1;
		
		private var isRoot:Boolean;

		private var states : Dictionary = new Dictionary();
		
		private var activeState : Controller = null;
		private var nextState : Controller = null;
		
		public static const SIG_ONADDTOSTAGE:String = "onAddToStage";
		public static const SIG_ONTICK:String = "onTick";
		public static const SIG_ONEXITSTATE:String = "onExitState";
		public static const SIG_ONENTERSTATE:String = "onEnterState";
		
		public function StateMachine( mainSprite:Sprite, isRoot:Boolean=true )
		{
			if( isRoot ) Oboe.root = this;
			
			super( mainSprite );
			
			this.isRoot = isRoot;
			
			if( isRoot )
			{
				mainSprite.addEventListener( Event.ADDED_TO_STAGE, onAddToStage );
				mainSprite.addEventListener( Event.ENTER_FRAME, onEnterFrame );
				if( Oboe.flags["DEBUG"] || Oboe.flags["SHOW_CONSOLE"] )
				{
					this.addChild( new DebugConsole() );
				}
			}
		}
		
		public function addState( key:Object, state:Controller ):void
		{
			states[key] = state;
		}
		
		public function setState( key:Object ):void
		{
			this.nextState = states[key];
			if( !this.isRoot ) this.applyStateChanges();
		}
		
		public function applyStateChanges():void
		{
			if( this.nextState != this.activeState )
			{
				this.addChild( this.nextState );
				if( this.activeState )
				{
					this.activeState.message( SIG_ONEXITSTATE );
					this.removeChild( this.activeState );
				}
				this.activeState = this.nextState;
				this.activeState.message( SIG_ONENTERSTATE );
				this.onChangeState( this.activeState );
			}
		}
		
		private function onAddToStage(e:Event):void
		{
			if( Oboe.stage ) return;
			
			if( this.isRoot ) this.onAddToRoot();
			
			Oboe.stage = this.getDisplayObject().stage;
			if( !Oboe.flags["stageWidth"] ) Oboe.flags["stageWidth"] = Oboe.stage.stageWidth;
			if( !Oboe.flags["stageHeight"] ) Oboe.flags["stageHeight"] = Oboe.stage.stageHeight;
			
			this.shortSignal( SIG_ONADDTOSTAGE );
		}
		
		private function onEnterFrame( e:Event ):void
		{
			this.applyStateChanges();
			var now : int = flash.utils.getTimer();
			var time : Number = (now - _baseTime)/1000.0;
			_baseTime = now;
			this.onPreTick( time );
			this.onTick( time );
			this.onPostTick( time );
			if( Oboe.flags["TRACE_FPS"] )
			{
				this.frameCount++;
				time = (now - lastFPSTraceTime)/1000.0; 
				if( time > 10 )
				{
					lastFPSTraceTime = now;
					now = int(this.frameCount/time+0.5);
					if( now != previousFPS )
					{
						Oboe.flags.FPS = now;
						trace( "FPS: ", now , "   CACHE SIZE:", Frame.getFrameCount() );
						previousFPS = now;
					}
					this.frameCount = 0;
				}
			}
		}
		
		protected function onPreTick( time:Number ):void
		{
		}
		protected function onTick( time:Number ):void
		{
			this.broadcastSignal( SIG_ONTICK, time);
		}
		protected function onPostTick( time:Number ):void
		{
		}
		protected function onChangeState( state:Controller ):void
		{
		}
	}
}